﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Transactions;
using Microsoft.Practices.Unity;
using MiniStock.BusinessLogic.EventQueue.Data;
using MiniStock.BusinessLogic.EventQueue.Queue;
using MiniStock.Entities.Common.Events;
using MiniStock.Infrastructure;
using MiniStock.Infrastructure.Configurator;

namespace MiniStock.BusinessLogic.EventDispatcher
{
    public class EventProcessorCommunicationLogic : BusinessLogicBase
    {
        //[Dependency]
        public IEventProcessorRepository Repository { get; set; }

        public void RegisterEventProcessor(string endpointAddress)
        {
            UnregisterEventProcessor(endpointAddress);
            Repository.Add(new EventQueueEventProcessorData
                               {
                                   Id = Guid.NewGuid(),
                                   EndpointAddress = endpointAddress,
                                   LastFetchSuccess = true,
                                   FailCount = 0,
                                   CreateDate = DateTime.Now,
                                   NextFetchDate = DateTime.Now,
                                   ClientHash = Guid.NewGuid().ToString(),
                               });
        }

        public void UnregisterEventProcessor(string endpointAddress)
        {
            var result = Repository.FirstOrDefault(i => i.EndpointAddress == endpointAddress);
            if (result == null) return;

            Repository.Delete(result);
        }

        public EventQueueEventProcessorData GetNextClientData()
        {
            using(var ts = new TransactionScope())
            {
                var result = Repository
                    .Where(i => i.NextFetchDate <= DateTime.Now)
                    .OrderBy(i => i.NextFetchDate)
                    .FirstOrDefault();
                
                if (result != null)
                {
                    result.NextFetchDate = DateTime.Now;
                    Repository.Commit();
                }
                
                ts.Complete();
                return result;
            }
        }

        public List<EventProcessorDescriptor> GetProcessorDescriptorForEntity(string entityHash)
        {
            if(ProcessorNodeMap == null)
                InitProcessorNodeMap();

            var result = ProcessorNodeMap.Where(i => i.IsProcessorNode && i.IsApplicableFor(entityHash)).ToList();
            return result;
        }

        public List<EventProcessorDescriptor> GetProcessorDescriptorForPersisters()
        {
            if (ProcessorNodeMap == null)
                InitProcessorNodeMap();

            var result = ProcessorNodeMap.Where(i => !i.IsProcessorNode).ToList();
            return result;
        }


        private List<EventProcessorDescriptor> ProcessorNodeMap { get; set; }

        public void InitProcessorNodeMap()
        {
            var nodeConfigurator = RootContainer.Resolve<IEventProcessorConfigurator>();

            ProcessorNodeMap = new List<EventProcessorDescriptor>();
            nodeConfigurator.InitEventProcessorDescriptors(ProcessorNodeMap);

            if(ProcessorNodeMap.Count(i => !i.IsProcessorNode) > 1)
            {
                throw new ApplicationException("Multiple processor nodes found");
            }
            ProcessorNodeMap.ForEach(i =>
                                         {
                                             if(i.IsProcessorNode && i.Plugins.Count == 0)
                                                 throw new ApplicationException("Processor node with no plugin found. Decrease processor node count");
                                         }
                );
        }

        public EventProcessorDescriptor LookupProcessor(string name)
        {
            if(String.IsNullOrEmpty(name))
                return null;

            if (ProcessorNodeMap == null)
                InitProcessorNodeMap();

            // Processor.1.0 --> Processor.1 (ignore thread number)
            var result = ProcessorNodeMap.FirstOrDefault(i => name.StartsWith(i.EventProcessorName));
            return result;
        }
    }
}
